﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using NLog;

namespace ConsoleService
{
    public class Instance
    {
        private static Logger logger = LogManager.GetCurrentClassLogger();

        public Server server { get; set; }
        public Process process { get; set; }

        public Instance(Server server)
        {
            this.server = server;
        }

        public Action CheckStatus()
        {
            var pid = this.GetPID();
            logger.Trace("{0}: Server {1} Current Status: Started {2}, Stopped {3}, Starting {4}, Stopping {5}, Updating {6}", "CheckStatus", this.server.server_id, this.server.is_started, this.server.is_stopped, this.server.is_starting, this.server.is_stopping, this.server.is_updating);
            if (pid <= 0 && (this.server.is_started || this.server.is_starting || (!this.server.is_stopped && !this.server.is_updating)))
            {
                this.server.is_started = false;
                this.server.is_stopped = true;
                this.server.is_stopping = false;
                this.server.is_starting = true;
                this.server.is_updating = false;
                logger.Trace("{0}: Server {1} Starting: {2}, {3}, {4}, {5}, {6}", "CheckStatus", this.server.server_id, this.server.is_started, this.server.is_stopped, this.server.is_starting, this.server.is_stopping, this.server.is_updating);
                return new Action() { server_id = this.server.server_id, action_datetime = DateTime.Now, action = "start" };
            }
            if (pid > 0 && (this.server.is_stopped || this.server.is_updating || this.server.is_stopping))
            {
                this.server.is_started = true;
                this.server.is_stopped = false;
                this.server.is_stopping = true;
                this.server.is_starting = false;
                this.server.is_updating = false;
                logger.Trace("{0}: Server {1} Stopping: {2}, {3}, {4}, {5}, {6}", "CheckStatus", this.server.server_id, this.server.is_started, this.server.is_stopped, this.server.is_starting, this.server.is_stopping, this.server.is_updating);
                return new Action() { server_id = this.server.server_id, action_datetime = DateTime.Now, action = "stop" };
            }
            return null;
        }

        public int GetPID()
        {
            logger.Trace("{0}: For server {1}", "GetPID", this.server.server_id);
            var pid = 0;
            if (process != null && !process.HasExited)
            {
                pid = process.Id;
                logger.Trace("{0}: Process Exists {1} ({2})", "GetPID", process.Id, process.HasExited);
            }
            if (pid == 0)
            {
                var pidFile = server.name + ".pid";
                if (System.Configuration.ConfigurationManager.AppSettings.AllKeys.Contains("PidFileName") &&
                    !string.IsNullOrWhiteSpace(System.Configuration.ConfigurationManager.AppSettings.Get("PidFileName")))
                {
                    pidFile = System.Configuration.ConfigurationManager.AppSettings.Get("PidFileName");
                }
                var path = System.IO.Path.Combine(server.config_path, pidFile);
                logger.Trace("{0}: Looking for PID File for server {1}", "GetPID", this.server.server_id);
                if (System.IO.File.Exists(path))
                {
                    logger.Trace("{0}: PID File Exists {1} for server {2}", "GetPID", path, this.server.server_id);
                    var content = System.IO.File.ReadAllText(path);
                    if (!int.TryParse(content, out pid))
                    {
                        pid = 0;
                    }
                }
                if (pid == 0)
                {
                    var processes = System.Diagnostics.Process.GetProcessesByName(System.IO.Path.GetFileNameWithoutExtension(server.executable));
                    if (processes != null && processes.Length >= 0)
                    {
                        logger.Trace("{0}: Found {1} processes for server {2}", "GetPID", processes.Length, this.server.server_id);
                        foreach (var found in processes)
                        {
                            if (found != null && !found.HasExited && found.Responding)
                            {
                                path = System.IO.Path.GetDirectoryName(found.MainModule.FileName);
                                if (path.EndsWith(System.IO.Path.AltDirectorySeparatorChar.ToString())) { path = path.Substring(0, path.Length - 1); }
                                if (!path.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString())) { path += System.IO.Path.DirectorySeparatorChar.ToString(); };
                                logger.Trace("{0}: Testing path {1} for server {2}", "GetPID", path, this.server.server_id);
                                if (path.Equals(server.path, StringComparison.InvariantCultureIgnoreCase))
                                {
                                    logger.Trace("{0}: Process found by name {1}", "GetPID", processes[0].StartInfo.FileName);
                                    pid = processes[0].Id;
                                    break;
                                };
                            };
                        };
                    }
                    else
                    {
                        logger.Trace("{0}: No processes named {1} for server {2}", "GetPID", System.IO.Path.GetFileNameWithoutExtension(server.executable), this.server.server_id);
                    }
                }
            }
            if (pid > 0 && (process == null || process.HasExited))
            {
                try
                {
                    process = Process.GetProcessById(pid);
                } catch { }
                if (process == null || process.HasExited || !process.ProcessName.Equals(System.IO.Path.GetFileNameWithoutExtension(server.executable), StringComparison.InvariantCultureIgnoreCase))
                {
                    logger.Trace("{0}: Process cannot be linked {1} ({2} / {3})", "GetPID", pid, (process == null), (process == null || process.HasExited));
                    pid = 0;
                    if (process != null)
                    {
                        process.Dispose();
                    }
                    process = null;
                }
                else
                {
                    logger.Trace("{0}: Process Linked {1} ({2})", "GetPID", process.Id, process.HasExited);
                }
            }
            logger.Trace("{0}: {1} for server {2}", "GetPID", pid, this.server.server_id);
            return pid;
        }
    }
}
